﻿// Sequin.WebLogs.UserIdentity
//
// (c) 2011 Arthur Pitman
//
// Part of the Sequin Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License (Version 2.1) as published by the Free Software Foundation.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License (Version 2.1) along with this library; if not, write to the 
// Free Software Foundation, Inc., 
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
// or see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace Sequin.WebLogs
{
	/// <summary>
	/// Represents the identity of user, i.e. the combination of their IP address and user agent
	/// </summary>
	public class UserIdentity : IComparable
	{
		/// <summary>
		/// Static collection of user agent to id mappings
		/// </summary>
		protected static Dictionary<string, int> mapUserAgentToId = new Dictionary<string, int>();

		/// <summary>
		/// Static collection of (inverse) id to user agent mappings
		/// </summary>
		protected static List<string> mapIdToUserAgent = new List<string>();

		/// <summary>
		/// The <see cref="IPAddress"/> component of the <see cref="UserIdentity"/>
		/// </summary>
		public IPAddress Ip { get; protected set; }

		/// <summary>
		/// The user agent id component of the <see cref="UserIdentity"/>
		/// </summary>
		public int UserAgentId { get; protected set; }

		/// <summary>
		/// Initializes a new <see cref="UserIdentity"/> instance with the specified <see cref="IPAddress"/> and user agent id
		/// </summary>
		/// <param name="ip">The user's <see cref="IPAddress"/></param>
		/// <param name="userAgent">The user's user agent id</param>
		public UserIdentity(IPAddress ip, int userAgent)
		{
			Ip = ip;
			UserAgentId = userAgent;
		}

		/// <summary>
		/// Returns the user agent <see cref="string"/> associated with the specified id
		/// </summary>
		/// <param name="userAgent"></param>
		/// <returns></returns>
		public static int MapUserAgentToId(string userAgent)
		{
			int userAgentId = 0;
			if (!mapUserAgentToId.TryGetValue(userAgent, out userAgentId))
			{
				userAgentId = mapUserAgentToId.Count();
				mapUserAgentToId.Add(userAgent, userAgentId);
				mapIdToUserAgent.Add(userAgent);
			}
			return userAgentId;
		}

		/// <summary>
		/// Returns the user agent <see cref="string"/> associated with the specified id
		/// </summary>
		/// <param name="id">the user agent id</param>
		/// <returns>The user agent <see cref="string"/></returns>
		public static string GetUserAgentById(int id)
		{
			if (id >= mapIdToUserAgent.Count)
				return null;
			return mapIdToUserAgent[id];            
		}

		/// <summary>
		/// Compares this <see cref="UserIdentity"/> to the specified <see cref="UserIdentity"/> object
		/// </summary>
		/// <param name="target">The <see cref="UserIdentity"/> to compare</param>
		/// <returns>An integer value indicating the lexical relationship between the two <see cref="UserIdentity"/> objects</returns>
		public int CompareTo(object target)
		{
			if (target is UserIdentity)
			{
				UserIdentity w = (UserIdentity)target;
				if (this.UserAgentId == w.UserAgentId)
				{
					return Ip.ToString().CompareTo(w.Ip.ToString());
				}
				else
				{
					return this.UserAgentId - w.UserAgentId;
				}
			}
			else
			{
				throw new ArgumentException("Expected CompareTo with UserIdentity");
			}
		}

		/// <summary>
		/// Checks if this <see cref="UserIdentity"/> object is equal to the specified <see cref="UserIdentity"/> object
		/// </summary>
		/// <param name="target">The <see cref="UserIdentity"/> to compare</param>
		/// <returns><c>true</c> if the two <see cref="UserIdentity"/> objects are equal</returns>
		public override bool Equals(object target)
		{
			if (target is UserIdentity)
			{
				UserIdentity w = (UserIdentity)target;                
				return (this.UserAgentId == w.UserAgentId) && Ip.Equals(w.Ip);
			}
			else
			{
				throw new ArgumentException("Expected Equals with UserIdentity");
			}
		}

		/// <summary>
		/// Create a hash code for this <see cref="UserIdentity"/>
		/// </summary>
		/// <returns>The integer hash code</returns>
		public override int GetHashCode()
		{
			return UserAgentId.GetHashCode() + Ip.GetHashCode();
		}
	}
}
